package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.content.model.dto.TeachplanMediaDTO;
import com.xuecheng.api.media.model.dto.MediaDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.AuditEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.content.agent.MediaApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.predicate.CourseAuditStatusPredicate;
import com.xuecheng.content.convert.TeachPlanConvert;
import com.xuecheng.content.convert.TeachplanMediaConvert;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ext.TeachPlanNode;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author 杨佳畅
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private TeachplanMediaService teachplanMediaService;

    @Autowired
    private MediaApiAgent mediaApiAgent;

    /**
     * 查询课程计划树
     *
     * @param courseId
     * @param companyId
     * @return
     */
    @Override
    public TeachplanDTO queryTreeNodes(Long courseId, Long companyId) {
        // 校验关键数据
        courseBaseService.getCourseBaseById(courseId, companyId);

        // 查询一门课程的课程计划
        TeachplanMapper baseMapper = this.getBaseMapper();
        List<TeachPlanNode> nodes = baseMapper.selectTreeNodeByCourseId(courseId);

        TeachplanDTO teachplanDTO = null;
        if (CollectionUtils.isEmpty(nodes)) {
            // 若数据为空，默认给一个空对象
            teachplanDTO = new TeachplanDTO();
        } else {
            // 获取一级课程计划数据，并将一级课程计划数据从集合中删除
            TeachPlanNode rootNode = nodes.remove(0);
            generateTreeNodes(rootNode, nodes);
            // 将树形结构转换为DTO返回
            teachplanDTO = TeachPlanConvert.INSTANCE.node2dto(rootNode);
        }

        return teachplanDTO;
    }

    @Override
    @Transactional
    public TeachplanDTO createOrModifyTeachplan(TeachplanDTO dto, Long companyId) {

        TeachplanDTO resultDTO = null;

        if (ObjectUtils.isEmpty(dto.getTeachPlanId())) {
            // 新增
            resultDTO = createTeachplan(dto, companyId);
        } else {
            // 修改
            resultDTO = modifyTeachplan(dto, companyId);
        }
        return resultDTO;
    }

    /**
     * 添加课程计划
     *
     * @param dto
     * @param companyId
     * @return
     */
    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120027);
        }
        // 只能对未提交、审核未通过的课程添加课程计划
        CourseBaseDTO courseBase = checkAuditStatus(dto, companyId);

        // 获得课程计划父级数据
        Teachplan parentNode = getParentTeachPlan(dto, courseBase);

        dto.setParentid(parentNode.getId());
        dto.setGrade(parentNode.getGrade() + 1);

        TeachplanMapper baseMapper = this.getBaseMapper();
        Integer orderBy = baseMapper.selectOrderByParentId(parentNode.getId());

        if (ObjectUtils.isEmpty(orderBy)) {
            dto.setOrderby(TeachPlanEnum.FIRST_LEVEL);
        } else {
            dto.setOrderby(orderBy + 1);
        }

        // 将dto转为po并保存到数据库
        Teachplan teachplan = TeachPlanConvert.INSTANCE.dto2entity(dto);

        boolean result = this.save(teachplan);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        // 将数据库最新的数据转为DTO返回
        Teachplan po = this.getById(teachplan.getId());
        TeachplanDTO resultDTO = TeachPlanConvert.INSTANCE.entity2dto(po);

        return resultDTO;
    }

    /**
     * 修改课程计划
     *
     * @param dto
     * @param companyId
     * @return
     */
    private TeachplanDTO modifyTeachplan(TeachplanDTO dto, Long companyId) {
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120027);
        }
        // 只能对未提交、审核未通过的课程修改课程计划
        checkAuditStatus(dto, companyId);

        // 课程计划是否存在
        Teachplan teachplan = this.getById(dto.getTeachPlanId());
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        // 教学机构只能操作二级和三级课程计划
        // 不允许修改一级数据
        if (TeachPlanEnum.FIRST_LEVEL.equals(teachplan.getParentid())) {
            ExceptionCast.cast(ContentErrorCode.E_120022);
        }

        // 修改课程计划
        // 使用 LambdaUpdateWrapper，mp的自动填充功能会失效
        LambdaUpdateWrapper<Teachplan> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Teachplan::getPname, dto.getPname());
        updateWrapper.set(Teachplan::getStartTime, dto.getStartTime());
        updateWrapper.set(Teachplan::getEndTime, dto.getEndTime());
        updateWrapper.set(Teachplan::getIsPreview, dto.getIsPreview());
        updateWrapper.set(Teachplan::getChangeDate, dto.getChangeDate());
        updateWrapper.eq(Teachplan::getId, dto.getTeachPlanId());
        boolean updateResult = this.update(updateWrapper);
        if (!updateResult) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }

        // 将数据库最新的数据返回
        Teachplan teachplanPo = this.getById(dto.getTeachPlanId());
        TeachplanDTO teachplanDTO = TeachPlanConvert.INSTANCE.entity2dto(teachplanPo);
        return teachplanDTO;
    }

    /**
     * 删除课程计划
     *
     * @param teachPlanId 课程计划ID
     * @param companyId   机构ID
     */
    @Override
    public void removeTeachPlan(Long teachPlanId, Long companyId) {
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120027);
        }
        // 查询课程计划信息
        Teachplan teachplan = this.getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }

        TeachplanDTO dto = TeachPlanConvert.INSTANCE.entity2dto(teachplan);
        // 只可以删除未提交、审核未通过的课程
        CourseBaseDTO courseBase = checkAuditStatus(dto, companyId);

        // 判断机构ID是否匹配
        if (!(ObjectUtils.nullSafeEquals(courseBase.getCompanyId(), companyId))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }

        // 判断课程等级
        if (teachplan.getGrade() == TeachPlanEnum.SECEND_LEVEL) {
            // 判断二级课程计划是否有子级课程计划
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, teachPlanId);
            TeachplanMapper baseMapper = this.baseMapper;
            List<Teachplan> teachplanList = baseMapper.selectList(queryWrapper);
            if (!ObjectUtils.isEmpty(teachplanList)) {
                ExceptionCast.cast(ContentErrorCode.E_120023);
            }
        } else {
            // 判断三级课程计划是否有媒资信息
            LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TeachplanMedia::getTeachplanId, teachPlanId);
            TeachplanMedia media = teachplanMediaService.getOne(queryWrapper);
            if (!ObjectUtils.isEmpty(media)) {
                ExceptionCast.cast(ContentErrorCode.E_120416);
            }
        }

        // 根据ID删除课程计划
        this.removeById(teachPlanId);
    }

    /**
     * 课程计划绑定媒资信息
     *
     * @param dto
     * @param companyId
     * @return
     */
    @Override
    @Transactional
    public TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto, Long companyId) {
        if (ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(ContentErrorCode.E_120027);
        }
        // 查询课程计划信息
        Teachplan teachplan = this.getById(dto.getTeachplanId());
        // 课程计划是否存在
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        // 只能对三级课程计划添加媒资
        if (!(TeachPlanEnum.THIRD_LEVEL.equals(teachplan.getGrade()))) {
            ExceptionCast.cast(ContentErrorCode.E_120410);
        }

        // 只能对未发布、审核未通过的课程计划添加媒资
        TeachplanDTO teachplanDTO = TeachPlanConvert.INSTANCE.entity2dto(teachplan);
        CourseBaseDTO courseBase = checkAuditStatus(teachplanDTO, companyId);

        // 只有录播课程才可以关联媒资
        if (!(CourseModeEnum.COURSE_MODE_RECORD_STATUS.getCode().equals(courseBase.getTeachmode()))) {
            ExceptionCast.cast(ContentErrorCode.E_120418);
        }

        // 查询媒资信息 - 远程调用
        RestResponse<MediaDTO> restResponse = mediaApiAgent.getMediaById4Service(dto.getMediaId());
        if (!(restResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(restResponse.getCode(), restResponse.getMsg());
        }
        MediaDTO media = restResponse.getResult();
        // 是否为同一家机构
        if (!(ObjectUtils.nullSafeEquals(companyId, media.getCompanyId()))) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
        // 已审核通过的媒资才可以绑定课程计划
        if (!(AuditEnum.AUDIT_PASTED_STATUS.getCode()).equals(media.getAuditStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }

        // 课程计划是否绑定了媒资信息
        LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TeachplanMedia::getTeachplanId, teachplan.getId());
        queryWrapper.eq(TeachplanMedia::getCourseId, teachplan.getCourseId());
        int count = teachplanMediaService.count(queryWrapper);
        if (count > 0) {
            ExceptionCast.cast(ContentErrorCode.E_120416);
        }

        // 若一个三级课程计划还没有绑定媒资信息，就可以创建绑定信息保存到数据库了
        TeachplanMedia teachplanMedia = new TeachplanMedia();
        teachplanMedia.setTeachplanId(dto.getTeachplanId());
        teachplanMedia.setMediaId(dto.getMediaId());
        teachplanMedia.setCourseId(teachplan.getCourseId());
        teachplanMedia.setMediaFilename(media.getFilename());

        boolean saveResult = teachplanMediaService.save(teachplanMedia);
        if (!saveResult) {
            ExceptionCast.cast(ContentErrorCode.E_120411);
        }

        // 返回DTO
        TeachplanMediaDTO resultDTO = TeachplanMediaConvert.INSTANCE.entity2dto(teachplanMedia);
        return resultDTO;
    }

    /**
     * 检查课程审核状态，用于添加/修改课程计划
     */
    private CourseBaseDTO checkAuditStatus(TeachplanDTO dto, Long companyId) {
        // 获取课程基础信息
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(dto.getCourseId(), companyId);
        // 获取课程审核状态
        String auditStatus = courseBase.getAuditStatus();
        // 只能对未提交、审核未通过的课程添加课程计划
        boolean isCreateStatus = CourseAuditStatusPredicate.predicate(auditStatus, code -> CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(auditStatus) ||
                CourseAuditEnum.AUDIT_DISPAST_STATUS.getCode().equals(auditStatus));
        ExceptionCast.cast(!isCreateStatus, ContentErrorCode.E_120015);
        return courseBase;
    }

    /**
     * 获取父级课程计划
     */
    private Teachplan getParentTeachPlan(TeachplanDTO dto, CourseBaseDTO courseBase) {
        // 判断新增数据是二级还是三级
        Long parentId = dto.getParentid();
        // 如果没有：二级课程计划
        if (ObjectUtils.isEmpty(parentId)) {
            // 获得二级父数据
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);

            Teachplan parentNode = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(parentNode)) {
                // 创建课程计划一级数据
                parentNode = new Teachplan();
                parentNode.setPname(courseBase.getName());
                // 一级课程计划，parentId 为 0
                parentNode.setParentid(Long.valueOf(TeachPlanEnum.FIRST_PARENTID_FLAG));
                parentNode.setGrade(TeachPlanEnum.FIRST_LEVEL);
                parentNode.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                parentNode.setCourseId(courseBase.getCourseBaseId());

                boolean result = this.save(parentNode);

                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120415);
                }
            }
            return parentNode;
        } else { // 如果有：三级课程计划
            // 获得三级父数据
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getId, parentId);
            queryWrapper.eq(Teachplan::getCourseId, courseBase.getCourseBaseId());

            Teachplan parentNode = this.getOne(queryWrapper);
            if (ObjectUtils.isEmpty(parentNode)) {
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }
            return parentNode;
        }
    }

    /**
     * 递归生成课程计划树
     */
    private void generateTreeNodes(TeachPlanNode parent, List<TeachPlanNode> nodes) {

        // 判断父级的集合树形是否为空，若为空需要创建出来，防止空指针
        if (CollectionUtils.isEmpty(parent.getChildrenNodes())) {
            parent.setChildrenNodes(Lists.newArrayList());
        }

        // 遍历集合数据
        nodes.forEach(node -> {
            // 若父级.id = 子级.parentId 则加入子集合中
            if (ObjectUtils.nullSafeEquals(parent.getId(), node.getParentid())) {
                parent.getChildrenNodes().add(node);

                // 若课程计划等级不是 3 级，才进行递归
                if (!(TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade()))) {
                    // 递归生成树
                    generateTreeNodes(node, nodes);
                } else {
                    // 若是三级课程计划，应该查询是否有关联的媒资信息
                    LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(TeachplanMedia::getTeachplanId, node.getId());
                    TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);
                    if (!ObjectUtils.isEmpty(teachplanMedia)) {
                        node.setTeachplanMedia(teachplanMedia);
                    }
                }
            }
        });
    }

}
